package mysgoog.brown;

import static java.lang.System.in;
import static java.lang.System.out;
import java.io.*;
import java.text.*;
import java.util.*;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * This is Brown, a simple go program.                           *
 *                                                               *
 * Copyright 2003 and 2004 by Gunnar Farneb�ck.                  *
 *                                                               *
 * Permission is hereby granted, free of charge, to any person   *
 * obtaining a copy of this file gtp.c, to deal in the Software  *
 * without restriction, including without limitation the rights  *
 * to use, copy, modify, merge, publish, distribute, and/or      *
 * sell copies of the Software, and to permit persons to whom    *
 * the Software is furnished to do so, provided that the above   *
 * copyright notice(s) and this permission notice appear in all  *
 * copies of the Software and that both the above copyright      *
 * notice(s) and this permission notice appear in supporting     *
 * documentation.                                                *
 *                                                               *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY     *
 * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE    *
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR       *
 * PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO      *
 * EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS  *
 * NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR    *
 * CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING    *
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF    *
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT    *
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS       *
 * SOFTWARE.                                                     *
 *                                                               *
 * Except as contained in this notice, the name of a copyright   *
 * holder shall not be used in advertising or otherwise to       *
 * promote the sale, use or other dealings in this Software      *
 * without prior written authorization of the copyright holder.  *
 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public class Interface
{
    /* Forward declarations. */
    /* List of known commands. */
    static private GtpCommand[] commands = { new GtpCommand("protocol_version")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            return Gtp.gtp_success("2");
        }
    }, new GtpCommand("name")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            return Gtp.gtp_success("Brown");
        }
    }, new GtpCommand("version")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            return Gtp.gtp_success(Brown.VERSION_STRING);
        }
    }, new GtpCommand("known_command")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            // TODO ignored BUFSIZE
            Scanner ss = new Scanner(cmd);
            if (!ss.hasNext())
                return Gtp.gtp_success("false");
            String command_name = ss.next();
            for (GtpCommand gc : commands)
            {
                if (command_name.equals(gc.name))
                    return Gtp.gtp_success("true");
            }
            return Gtp.gtp_success("false");
        }
    }, new GtpCommand("list_commands")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            Gtp.gtp_start_response(Gtp.GTP_SUCCESS);
            for (GtpCommand gc : commands)
            {
                if (gc != null)
                    Gtp.gtp_printf(gc.name + "\n");
            }
            Gtp.gtp_printf("\n");
            return Gtp.GTP_OK;
        }
    }, new GtpCommand("quit")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            Gtp.gtp_success("");
            return Gtp.GTP_QUIT;
        }
    }, new GtpCommand("boardsize")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int boardsize;
            Scanner ss = new Scanner(cmd);
            if (!ss.hasNextInt())
                return Gtp.gtp_failure("boardsize not an integer");
            boardsize = ss.nextInt();
            if (boardsize < Brown.MIN_BOARD || boardsize > Brown.MAX_BOARD)
                return Gtp.gtp_failure("unacceptable size");
            Brown.set_board_size(boardsize);
            Gtp.gtp_internal_set_boardsize(boardsize);
            Brown.init_brown();
            return Gtp.gtp_success("");
        }
    }, new GtpCommand("clear_board")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            Brown.clear_board();
            return Gtp.gtp_success("");
        }
    }, new GtpCommand("komi")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            Scanner ss = new Scanner(cmd);
            if (!ss.hasNextDouble())
                return Gtp.gtp_failure("komi not a float");
            Brown.komi = ss.nextDouble();
            return Gtp.gtp_success("");
        }
    }, new GtpCommand("fixed_handicap")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            return place_handicap(cmd, true);
        }
    }, new GtpCommand("place_free_handicap")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            return place_handicap(cmd, false);
        }
    }, new GtpCommand("set_free_handicap")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int i = 0, j = 0;
            int n;
            int handicap = 0;
            if (!Brown.board_empty())
                return Gtp.gtp_failure("board not empty");
            int[] arrI = new int[] { i }, arrJ = new int[] { j };
            while ((n = Gtp.gtp_decode_coord(cmd, arrI, arrJ)) > 0)
            {
                i = arrI[0];
                j = arrJ[0];
                cmd = cmd.substring(n);
                if (Brown.get_board(i, j) != Brown.EMPTY)
                {
                    Brown.clear_board();
                    return Gtp.gtp_failure("repeated vertex");
                }
                Brown.play_move(i, j, Brown.BLACK);
                handicap++;
            }
            Scanner ss = new Scanner(cmd);
            if (ss.hasNext())
            {
                Brown.clear_board();
                return Gtp.gtp_failure("invalid coordinate");
            }
            if (handicap < 2 || handicap >= Brown.board_size * Brown.board_size)
            {
                Brown.clear_board();
                return Gtp.gtp_failure("invalid handicap");
            }
            return Gtp.gtp_success("");
        }
    }, new GtpCommand("play")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int[] i = new int[] { 0 }, j = new int[] { 0 };
            int[] color = new int[] { Gtp.EMPTY };
            if (Gtp.gtp_decode_move(cmd, color, i, j) == 0)
                return Gtp.gtp_failure("invalid color or coordinate");
            if (!Brown.legal_move(i[0], j[0], color[0]))
                return Gtp.gtp_failure("illegal move");
            Brown.play_move(i[0], j[0], color[0]);
            return Gtp.gtp_success("");
        }
    }, new GtpCommand("genmove")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int[] i = new int[] { 0 }, j = new int[] { 0 };
            int[] color = new int[] { Gtp.EMPTY };
            if (Gtp.gtp_decode_color(cmd, color) == 0)
                return Gtp.gtp_failure("invalid color");
            Brown.generate_move(i, j, color[0]);
            Brown.play_move(i[0], j[0], color[0]);
            Gtp.gtp_start_response(Gtp.GTP_SUCCESS);
            Gtp.gtp_print_vertex(i[0], j[0]);
            return Gtp.gtp_finish_response();
        }
    }, new GtpCommand("final_score")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            double score = Brown.komi;
            int i, j;
            Brown.compute_final_status();
            for (i = 0; i < Brown.board_size; i++)
                for (j = 0; j < Brown.board_size; j++)
                {
                    int status = Brown.get_final_status(i, j);
                    if (status == Brown.BLACK_TERRITORY)
                        score--;
                    else if (status == Brown.WHITE_TERRITORY)
                        score++;
                    else if ((status == Brown.ALIVE) ^ (Brown.get_board(i, j) == Brown.WHITE))
                        score--;
                    else
                        score++;
                }
            DecimalFormat df = new DecimalFormat();
            df.setMaximumFractionDigits(1);
            df.setMinimumFractionDigits(1);
            if (score > 0.0)
                return Gtp.gtp_success("W+" + df.format(score));
            if (score < 0.0)
                return Gtp.gtp_success("B+" + df.format(-score));
            return Gtp.gtp_success("0");
        }
    }, new GtpCommand("final_status_list")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int i, j;
            int status = Brown.UNKNOWN;
            String status_string;
            boolean first_string;
            Scanner ss = new Scanner(cmd);
            if (!ss.hasNext())
                return Gtp.gtp_failure("missing status");
            status_string = ss.next();
            if (!status_string.equals("alive"))
                status = Brown.ALIVE;
            else if (!status_string.equals("dead"))
                status = Brown.DEAD;
            else if (!status_string.equals("seki"))
                status = Brown.SEKI;
            else
                return Gtp.gtp_failure("invalid status");
            Brown.compute_final_status();
            Gtp.gtp_start_response(Gtp.GTP_SUCCESS);
            first_string = true;
            for (i = 0; i < Brown.board_size; i++)
                for (j = 0; j < Brown.board_size; j++)
                    if (Brown.get_final_status(i, j) == status)
                    {
                        int k;
                        int[] stonei = new int[Brown.MAX_BOARD * Brown.MAX_BOARD];
                        int[] stonej = new int[Brown.MAX_BOARD * Brown.MAX_BOARD];
                        int num_stones = Brown.get_string(i, j, stonei, stonej);
                        /*
                         * Clear the status so we don't find the string again.
                         */
                        for (k = 0; k < num_stones; k++)
                            Brown.set_final_status(stonei[k], stonej[k], Brown.UNKNOWN);
                        if (first_string)
                            first_string = false;
                        else
                            Gtp.gtp_printf("\n");
                        Gtp.gtp_print_vertices(num_stones, stonei, stonej);
                    }
            return Gtp.gtp_finish_response();
        }
    }, new GtpCommand("showboard")
    {
        @Override
        int execute(String cmd)
        {
            super.execute(cmd);
            int i, j;
            int[] symbols = { '.', 'O', 'X' };
            Gtp.gtp_start_response(Gtp.GTP_SUCCESS);
            Gtp.gtp_printf("\n");
            letters();
            for (i = 0; i < Brown.board_size; i++)
            {
                out.printf("\n%2d", Brown.board_size - i);
                for (j = 0; j < Brown.board_size; j++)
                    out.printf(" %c", symbols[Brown.get_board(i, j)]);
                out.printf(" %d", Brown.board_size - i);
            }
            out.printf("\n");
            letters();
            return Gtp.gtp_finish_response();
        }
    }, null };

    /* Common code for fixed_handicap and place_free_handicap. */
    static private int place_handicap(String s, boolean fixed)
    {
        int handicap;
        int m, n;
        boolean first_stone = true;
        if (!Brown.board_empty())
            return Gtp.gtp_failure("board not empty");
        Scanner ss = new Scanner(s);
        if (!ss.hasNextInt())
            return Gtp.gtp_failure("handicap not an integer");
        handicap = ss.nextInt();
        if (handicap < 2)
            return Gtp.gtp_failure("invalid handicap");
        if (fixed && !Brown.valid_fixed_handicap(handicap))
            return Gtp.gtp_failure("invalid handicap");
        if (fixed)
            Brown.place_fixed_handicap(handicap);
        else
            Brown.place_free_handicap(handicap);
        Gtp.gtp_start_response(Gtp.GTP_SUCCESS);
        for (m = 0; m < Brown.board_size; m++)
            for (n = 0; n < Brown.board_size; n++)
                if (Brown.get_board(m, n) != Gtp.EMPTY)
                {
                    if (first_stone)
                        first_stone = false;
                    else
                        Gtp.gtp_printf(" ");
                    Gtp.gtp_print_vertex(m, n);
                }
        return Gtp.gtp_finish_response();
    }

    /* Write a row of letters, skipping 'I'. */
    static private void letters()
    {
        int i;
        out.printf("  ");
        for (i = 0; i < Brown.board_size; i++)
        {
            int tmp = 0;
            if (i >= 8)
                tmp = 1;
            out.printf(" %c", 'A' + i + tmp);
        }
    }

    public static void main(String[] args)
    {

        Lib.log("Start");
        int random_seed = 1;
        /* Optionally a random seed can be passed as an argument to the program. */
        if (args.length > 0)
        {
            random_seed = Integer.parseInt(args[0]);
        }
        Lib.seedRandom(random_seed);
        /* Make sure that stdout is not block buffered. */
        // TODO setbuf(stdout, NULL);
        /* Inform the GTP utility functions about the initial board size. */
        Gtp.gtp_internal_set_boardsize(Brown.board_size);
        /* Initialize the board. */
        Brown.init_brown();
        /* Process GTP commands. */
        try
        {
            Gtp.gtp_main_loop(commands, in, null);
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
